"""
AI Agent Pro - 测试配置和工具
"""

import asyncio
import pytest
import pytest_asyncio
from typing import AsyncGenerator, Generator
from unittest.mock import AsyncMock, MagicMock

from fastapi.testclient import TestClient
from httpx import AsyncClient
from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

from app.main import app
from app.core.config import get_settings
from app.core.database import get_db, Base
from app.models import User, Agent, Conversation, Memory
from app.auth.security import PasswordManager, TokenManager

# 测试配置
TEST_DATABASE_URL = "sqlite+aiosqlite:///./test.db"
TEST_DATABASE_URL_SYNC = "sqlite:///./test.db"

settings = get_settings()

# 创建测试数据库引擎
test_async_engine = create_async_engine(
    TEST_DATABASE_URL,
    connect_args={"check_same_thread": False},
    poolclass=StaticPool,
    echo=False
)

test_sync_engine = create_engine(
    TEST_DATABASE_URL_SYNC,
    connect_args={"check_same_thread": False},
    poolclass=StaticPool,
    echo=False
)

TestSessionLocal = sessionmaker(
    test_sync_engine,
    autocommit=False,
    autoflush=False,
)

AsyncTestSessionLocal = sessionmaker(
    test_async_engine,
    class_=AsyncSession,
    autocommit=False,
    autoflush=False,
)


# ====== 测试夹具 ======

@pytest.fixture(scope="session")
def event_loop():
    """创建事件循环"""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope="function")
async def db_session() -> AsyncGenerator[AsyncSession, None]:
    """创建测试数据库会话"""
    # 创建所有表
    async with test_async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    # 创建会话
    async with AsyncTestSessionLocal() as session:
        try:
            yield session
        finally:
            await session.close()
    
    # 清理表
    async with test_async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)


@pytest.fixture(scope="function")
def sync_db_session() -> Generator:
    """创建同步测试数据库会话"""
    Base.metadata.create_all(bind=test_sync_engine)
    
    session = TestSessionLocal()
    try:
        yield session
    finally:
        session.close()
        Base.metadata.drop_all(bind=test_sync_engine)


@pytest.fixture(scope="function")
async def client(db_session: AsyncSession) -> AsyncGenerator[AsyncClient, None]:
    """创建测试客户端"""
    
    async def override_get_db():
        yield db_session
    
    app.dependency_overrides[get_db] = override_get_db
    
    async with AsyncClient(app=app, base_url="http://test") as ac:
        yield ac
    
    app.dependency_overrides.clear()


@pytest.fixture(scope="function")
def sync_client() -> TestClient:
    """创建同步测试客户端"""
    return TestClient(app)


# ====== 测试数据工厂 ======

class TestDataFactory:
    """测试数据工厂"""
    
    @staticmethod
    def create_test_user(**kwargs) -> dict:
        """创建测试用户数据"""
        default_data = {
            "username": "testuser",
            "email": "test@example.com",
            "password": "testpassword123",
            "full_name": "Test User",
            "is_active": True,
            "is_verified": True,
        }
        default_data.update(kwargs)
        return default_data
    
    @staticmethod
    async def create_user_in_db(session: AsyncSession, **kwargs) -> User:
        """在数据库中创建测试用户"""
        user_data = TestDataFactory.create_test_user(**kwargs)
        
        user = User(
            username=user_data["username"],
            email=user_data["email"],
            hashed_password=PasswordManager.hash_password(user_data["password"]),
            full_name=user_data.get("full_name"),
            is_active=user_data.get("is_active", True),
            is_verified=user_data.get("is_verified", True),
        )
        
        session.add(user)
        await session.commit()
        await session.refresh(user)
        
        return user
    
    @staticmethod
    def create_test_agent(**kwargs) -> dict:
        """创建测试智能体数据"""
        default_data = {
            "name": "测试智能体",
            "description": "这是一个测试智能体",
            "model": "gpt-4",
            "config": {
                "temperature": 0.7,
                "max_tokens": 2000,
                "thinking_enabled": True
            },
            "system_prompt": "你是一个友好的AI助手",
            "tools": ["web_search", "calculator"],
            "is_active": True,
            "is_public": False,
        }
        default_data.update(kwargs)
        return default_data
    
    @staticmethod
    async def create_agent_in_db(session: AsyncSession, user_id: str, **kwargs) -> Agent:
        """在数据库中创建测试智能体"""
        agent_data = TestDataFactory.create_test_agent(**kwargs)
        
        agent = Agent(
            user_id=user_id,
            name=agent_data["name"],
            description=agent_data.get("description"),
            model=agent_data["model"],
            config=agent_data["config"],
            system_prompt=agent_data.get("system_prompt"),
            tools=agent_data.get("tools"),
            is_active=agent_data.get("is_active", True),
            is_public=agent_data.get("is_public", False),
        )
        
        session.add(agent)
        await session.commit()
        await session.refresh(agent)
        
        return agent
    
    @staticmethod
    def create_test_conversation(**kwargs) -> dict:
        """创建测试对话数据"""
        default_data = {
            "title": "测试对话",
            "summary": "这是一个测试对话",
            "message_count": 0,
            "total_tokens": 0,
            "is_archived": False,
        }
        default_data.update(kwargs)
        return default_data
    
    @staticmethod
    def create_auth_headers(user_id: str, username: str = "testuser") -> dict:
        """创建认证头"""
        token = TokenManager.create_access_token(
            data={
                "sub": user_id,
                "username": username,
                "permissions": ["agent:read", "agent:create", "agent:execute"]
            }
        )
        return {"Authorization": f"Bearer {token}"}


# ====== Mock 工具 ======

class MockAIModel:
    """模拟AI模型"""
    
    def __init__(self, responses: list = None):
        self.responses = responses or ["这是一个模拟的AI回复"]
        self.call_count = 0
    
    async def generate(self, prompt: str, **kwargs) -> str:
        response = self.responses[self.call_count % len(self.responses)]
        self.call_count += 1
        return response
    
    async def generate_stream(self, prompt: str, **kwargs):
        response = await self.generate(prompt, **kwargs)
        words = response.split()
        for word in words:
            yield word + " "
    
    async def create_embedding(self, text: str, **kwargs) -> list:
        # 返回模拟的嵌入向量
        import random
        return [random.random() for _ in range(1536)]


class MockTool:
    """模拟工具"""
    
    def __init__(self, name: str, result: dict = None):
        self.name = name
        self.result = result or {"success": True, "data": "模拟工具结果"}
        self.call_count = 0
        self.call_history = []
    
    async def execute(self, **kwargs):
        self.call_count += 1
        self.call_history.append(kwargs)
        return self.result


# ====== 测试辅助函数 ======

async def assert_response_success(response, expected_status: int = 200):
    """断言响应成功"""
    assert response.status_code == expected_status, f"期望状态码 {expected_status}，实际 {response.status_code}：{response.text}"
    
    if response.headers.get("content-type", "").startswith("application/json"):
        data = response.json()
        if "error" in data:
            pytest.fail(f"响应包含错误: {data['error']}")
        return data
    
    return response.text


async def assert_response_error(response, expected_status: int = 400):
    """断言响应错误"""
    assert response.status_code == expected_status, f"期望状态码 {expected_status}，实际 {response.status_code}"
    
    if response.headers.get("content-type", "").startswith("application/json"):
        data = response.json()
        assert "error" in data or "detail" in data, "错误响应应包含错误信息"
        return data
    
    return response.text


def create_mock_agent_with_tools():
    """创建带工具的模拟智能体"""
    from app.agents.core import Agent, AgentConfig
    from app.tools.manager import ToolManager
    
    # 创建模拟工具
    tool_manager = ToolManager()
    search_tool = MockTool("web_search", {"success": True, "data": "搜索结果"})
    calc_tool = MockTool("calculator", {"success": True, "data": 42})
    
    tool_manager._tools = {
        "web_search": search_tool,
        "calculator": calc_tool
    }
    
    # 创建智能体配置
    config = AgentConfig(
        name="测试智能体",
        model="mock-gpt-4",
        tools=["web_search", "calculator"]
    )
    
    # 创建智能体
    agent = Agent(config, tools=tool_manager._tools)
    agent.model = MockAIModel()
    
    return agent, tool_manager


# ====== 性能测试工具 ======

import time
from contextlib import asynccontextmanager
from typing import Optional


@asynccontextmanager
async def measure_time(name: str = "操作", threshold: Optional[float] = None):
    """测量执行时间"""
    start_time = time.time()
    try:
        yield
    finally:
        duration = time.time() - start_time
        print(f"{name} 耗时: {duration:.3f}秒")
        
        if threshold and duration > threshold:
            pytest.fail(f"{name} 耗时 {duration:.3f}秒，超过阈值 {threshold}秒")


async def stress_test_endpoint(client: AsyncClient, endpoint: str, headers: dict = None, count: int = 100):
    """压力测试API端点"""
    start_time = time.time()
    
    tasks = []
    for _ in range(count):
        task = client.get(endpoint, headers=headers)
        tasks.append(task)
    
    responses = await asyncio.gather(*tasks, return_exceptions=True)
    
    duration = time.time() - start_time
    success_count = sum(1 for r in responses if not isinstance(r, Exception) and r.status_code == 200)
    
    print(f"压力测试结果:")
    print(f"  请求数量: {count}")
    print(f"  成功数量: {success_count}")
    print(f"  总耗时: {duration:.3f}秒")
    print(f"  平均响应时间: {duration/count:.3f}秒")
    print(f"  QPS: {count/duration:.1f}")
    
    return {
        "total_requests": count,
        "successful_requests": success_count,
        "total_time": duration,
        "average_time": duration / count,
        "qps": count / duration
    }


# ====== 测试标记 ======

# 使用pytest标记来分类测试
pytest_plugins = []

# 自定义标记
def pytest_configure(config):
    """配置pytest"""
    config.addinivalue_line("markers", "unit: 单元测试")
    config.addinivalue_line("markers", "integration: 集成测试")
    config.addinivalue_line("markers", "e2e: 端到端测试")
    config.addinivalue_line("markers", "slow: 慢速测试")
    config.addinivalue_line("markers", "auth: 认证相关测试")
    config.addinivalue_line("markers", "agent: 智能体相关测试")
    config.addinivalue_line("markers", "tool: 工具相关测试")
    config.addinivalue_line("markers", "memory: 记忆相关测试")


# 导出
__all__ = [
    # 夹具
    "db_session", "sync_db_session", "client", "sync_client",
    
    # 工厂
    "TestDataFactory",
    
    # Mock类
    "MockAIModel", "MockTool",
    
    # 辅助函数
    "assert_response_success", "assert_response_error",
    "create_mock_agent_with_tools", "measure_time", "stress_test_endpoint",
]